home *** CD-ROM | disk | FTP | other *** search
- /* This file contains code to implement the Routing Information Protocol (RIP)
- * and is derived from 4.2BSD code. Mike Karels of Berkeley has stated on
- * TCP-IP that the code may be freely used as long as UC Berkeley is
- * credited. (Well, here's some credit :-). AGB 4-28-88
-
- * Further documentation on the RIP protocol is now available in Charles
- * Hedrick's draft RFC, as yet unnumbered. AGB 5-6-88
- *
- * The RIP RFC has now been issued as RFC1058. AGB 7-23-88
- *
- * Code gutted and substantially rewritten. KA9Q 9/89
- *
- * Mods by PA0GRI
- *
- * Changes Copyright (c) 1993 Jeff White - N0POY, All Rights Reserved.
- * Permission granted for non-commercial copying and use, provided
- * this notice is retained.
- *
- * Rehack for RIP-2 (RFC1388) by N0POY 4/1993
- * Modules needed for changes: rip.c, rip.h, ripcmd.c, ripdump.c, ip.h
- * commands.h, iface.h, iface.c, version.c
- *
- * Beta release 11/16/93 V0.95
- *
- * Bug fix that prevented split horizon routing to work fixed.
- * 2/19/94 release V1.0
- *
- * G8BPQ's RIP98 added - G4HIP, 29/3/97
- */
-
- #include "global.h"
- #ifdef RIP
- #include <stdarg.h>
- #include <time.h>
- #include "mbuf.h"
- #include "netuser.h"
- #include "udp.h"
- #include "rip.h"
-
- #if !defined(_lint)
- static char rcsid[] OPTIONAL = "$Id: rip.c,v 1.25 1997/09/07 21:18:28 root Exp root $";
- #endif
-
- struct rip_stat Rip_stat = {{{0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}}, 0, 0, 0, 0, 0, 0};
- int16 Rip_trace;
- FILE *Rip_trace_file = NULLFILE;
- char *Rip_trace_fname = NULLCHAR;
- int Rip_merge;
- int32 Rip_ttl = RIP_TTL;
- int16 Rip_ver_refuse = 0;
- int Rip_default_refuse = FALSE;
- int rip98allow = 1;
- struct rip_list *Rip_list;
- struct udp_cb *Rip_cb;
- struct rip_auth *Rip_auth;
- struct rip_refuse *Rip_refuse;
-
- static int nbits (uint32 target);
- static void rip_trigger (void);
- static void rip_rx (struct iface * iface, struct udp_cb * sock, int16 cnt);
- static void proc_rip (struct iface * iface, uint32 gateway,
- struct rip_route * ep, unsigned char version);
- static char *putheader (unsigned char *cp, char command, char version, int16 domain);
- static char *putentry (char *cp, int16 fam, int16 tag, uint32 target,
- uint32 targmask, uint32 router, int32 metric);
- static char *put98entry (register char *cptr, uint32 target, int16 bits, int metric);
- static void send_routes (uint32 dest, int16 port, int trig,
- int flags, unsigned char version, struct rip_list * rdata);
-
- static void pullheader (struct rip_head * ep, struct mbuf ** bpp);
- static int check_authentication (struct rip_auth * auth,
- struct mbuf ** bpp, struct rip_head * header, uint32 srcaddr, char const *ifcname,
- struct rip_authenticate * entry);
- static char *putauth (char *cp, int16 authtype, char *authpass);
- static void rip_trace (short level, char const *errstr,...);
-
-
-
- /* Send RIP CMD_RESPONSE packet(s) to the specified rip_list entry */
-
- void
- rip_shout (void *p)
- {
- register struct rip_list *rl;
-
- rl = (struct rip_list *) p;
- stop_timer (&rl->rip_time);
- send_routes (rl->dest, RIP_PORT, 0, rl->flags, (unsigned char) rl->rip_version, rl);
- set_timer (&rl->rip_time, rl->interval * 1000L);
- start_detached_timer (&rl->rip_time);
- }
-
-
-
- /* Send the routing table. */
- static void
- send_routes (dest, port, trig, flags, version, rdata)
- uint32 dest; /* IP destination address to send to */
- int16 port;
- int trig; /* Send only triggered updates? */
- int flags;
- unsigned char version; /* Version of RIP packet */
- struct rip_list *rdata; /* Used for RIP-2 packets */
- {
- char *cp;
- int i, bits, numroutes, maxroutes;
- int16 pktsize;
- int riplen = RIP_ENTRY;
- struct mbuf *bp;
- struct route *rp;
- struct socket lsock, fsock;
- struct iface *iface;
-
- if (version == RIP_VERSION_98) { /* Fiddling to make RIP98 like latest official +1 */
- version = RIP_VERSION_X;
- riplen = RIP98_ENTRY;
- }
-
- if ((rp = rt_lookup (dest)) == NULLROUTE) {
- rip_trace (1, "No route to [%s] exists, cannot send", inet_ntoa (dest));
- return; /* No route exists, can't do it */
- }
- iface = rp->iface;
-
- /* Compute maximum packet size and number of routes we can send */
- pktsize = ip_mtu (dest) - IPLEN;
- pktsize = min (pktsize, RIP_PKTSIZE);
- maxroutes = (pktsize - RIP_HEADER) / riplen;
-
- lsock.address = INADDR_ANY;
- lsock.port = RIP_PORT;
- fsock.address = dest;
- fsock.port = port;
-
- /* Allocate space for a full size RIP packet and generate header */
- if ((bp = alloc_mbuf (pktsize)) == NULLBUF)
- return;
- numroutes = 0;
-
- /* See if we know information about what to send out */
-
- if ((version >= RIP_VERSION_2) && (rdata != NULLRL) && (version != RIP_VERSION_X)) {
- cp = putheader (bp->data, RIPCMD_RESPONSE, (char) version, rdata->domain);
- /* See if we need to put an authentication header on */
- if (flags & RIP_AUTHENTICATE) {
- cp = putauth (cp, RIP_AUTH_SIMPLE, rdata->rip_auth_code);
- numroutes++;
- }
- } else {
- if (version != RIP_VERSION_X)
- cp = putheader (bp->data, RIPCMD_RESPONSE, (char) version, 0);
- else
- cp = putheader (bp->data, RIPCMD_RESPONSE, RIP_VERSION_98, 0);
- }
-
- /* Emit route to ourselves, if requested */
- if (flags & RIP_US) {
- switch (version) {
- case RIP_VERSION_1:
- cp = putentry (cp, RIP_AF_INET, 0, iface->addr, 0, 0, 1);
- break;
-
- case RIP_VERSION_X:
- cp = put98entry (cp, iface->addr, 32, 1);
- break;
- default:
- cp = putentry (cp, RIP_AF_INET, 0, iface->addr, 0xFFFFFFFFUL, 0, 1);
- break;
- }
- numroutes ++;
- }
- /* Emit default route, if appropriate */
-
- if (R_default.iface != NULLIF && !(R_default.flags & RTPRIVATE)
- && (!trig || (R_default.flags & RTTRIG))) {
- if (!(flags & RIP_SPLIT) || iface != R_default.iface) {
- switch (version) {
- case RIP_VERSION_1:
- cp = putentry (cp, RIP_AF_INET, 0, 0, 0, 0, R_default.metric);
- break;
-
- case RIP_VERSION_X:
- cp = put98entry (cp, 0, 0, R_default.metric);
- break;
-
- default:
- cp = putentry (cp, RIP_AF_INET, R_default.route_tag, 0, 0, 0, R_default.metric);
- break;
- }
- numroutes++;
- } else if (trig && (flags & RIP_POISON)) {
- /* Poisoned reverse */
- switch (version) {
- case RIP_VERSION_1:
- cp = putentry (cp, RIP_AF_INET, 0, 0, 0, 0, RIP_METRIC_UNREACHABLE);
- break;
-
- case RIP_VERSION_X:
- cp = put98entry (cp, 0, 0, RIP_METRIC_UNREACHABLE);
- break;
-
- default:
- cp = putentry (cp, RIP_AF_INET, R_default.route_tag, 0, 0, 0, RIP_METRIC_UNREACHABLE);
- break;
- }
- numroutes++;
- }
- }
- for (bits = 0; bits < 32; bits++) {
- for (i = 0; i < HASHMOD; i++) {
- for (rp = Routes[bits][i]; rp != NULLROUTE; rp = rp->next) {
- if ((rp->flags & RTPRIVATE) || (trig && !(rp->flags & RTTRIG)))
- continue;
- /* With RIP 98, don't send a host entries where we have it marked as GW */
- if ((version == RIP_VERSION_X) && (rp->gateway == dest))
- continue;
-
- if (numroutes >= maxroutes) {
- /* Packet full, flush and make another */
- bp->cnt = (int16) (RIP_HEADER + numroutes * riplen);
- (void) send_udp (&lsock, &fsock, 0, 0, bp, bp->cnt, 0, 0);
- Rip_stat.vdata[(int) version].output++;
- if ((bp = alloc_mbuf (pktsize)) == NULLBUF)
- return;
- numroutes = 0;
-
- if ((version >= RIP_VERSION_2) && (rdata != NULLRL) && (version != RIP_VERSION_X)) {
- cp = putheader (bp->data, RIPCMD_RESPONSE, (char) version,
- rdata->domain);
- /* See if we need to put an authentication header on */
- if (flags & RIP_AUTHENTICATE) {
- cp = putauth (cp, RIP_AUTH_SIMPLE, rdata->rip_auth_code);
- numroutes++;
- }
- } else {
- if (version != RIP_VERSION_X)
- cp = putheader (bp->data, RIPCMD_RESPONSE, (char) version, 0);
- else
- cp = putheader (bp->data, RIPCMD_RESPONSE, RIP_VERSION_98, 0);
- }
- }
- if (!(flags & RIP_SPLIT) || iface != rp->iface) {
- switch (version) {
- case RIP_VERSION_1:
- cp = putentry (cp, RIP_AF_INET, 0, rp->target, 0, 0, rp->metric);
- break;
-
- case RIP_VERSION_X:
- cp = put98entry (cp, rp->target, (int16) rp->bits, rp->metric);
- break;
-
- default:
- cp = putentry (cp, RIP_AF_INET, rp->route_tag, rp->target,
- (0xFFFFFFFFUL << (32 - rp->bits)), rdata ? rdata->proxy_route : 0,
- rp->metric);
- break;
- }
- numroutes++;
- } else if (trig && (flags & RIP_POISON)) {
- switch (version) {
- case RIP_VERSION_1:
- cp = putentry (cp, RIP_AF_INET, 0, rp->target, 0, 0, RIP_METRIC_UNREACHABLE);
- break;
-
- case RIP_VERSION_X:
- cp = put98entry (cp, rp->target, (int16) rp->bits, RIP_METRIC_UNREACHABLE);
- break;
-
- default:
- cp = putentry (cp, RIP_AF_INET, rp->route_tag, rp->target,
- (0xFFFFFFFFUL << (32 - rp->bits)), rdata ? rdata->proxy_route : 0,
- RIP_METRIC_UNREACHABLE);
- break;
- }
- numroutes++;
- }
- }
- }
- }
- if (numroutes != 0) {
- bp->cnt = (int16) (RIP_HEADER + numroutes * riplen);
- (void) send_udp (&lsock, &fsock, 0, 0, bp, bp->cnt, 0, 0);
- Rip_stat.vdata[(int) version].output++;
- } else
- free_p (bp);
- }
-
-
-
- /* Add an entry to the rip broadcast list */
-
- int
- rip_add (dest, interval, flags, version, authpass, domain, route_tag, proxy)
- uint32 dest;
- int32 interval;
- char flags;
- char version;
- char authpass[RIP_AUTH_SIZE];
- int16 domain;
- int16 route_tag;
- uint32 proxy;
- {
- register struct rip_list *rl;
- struct route *rp;
-
- for (rl = Rip_list; rl != NULLRL; rl = rl->next)
- if ((rl->dest == dest) && (rl->domain == domain))
- return 0;
-
- if ((rp = rt_lookup (dest)) == NULLROUTE) {
- tprintf ("%s is unreachable\n", inet_ntoa (dest));
- return 0;
- }
- /* get a chunk of memory for the rip interface descriptor */
- rl = (struct rip_list *) callocw (1, sizeof (struct rip_list));
-
- /* tack this record on as the first in the list */
- rl->next = Rip_list;
- if (rl->next != NULLRL)
- rl->next->prev = rl;
- Rip_list = rl;
-
- rl->dest = dest;
-
- rip_trace (9, "Rip added V%d Flags %d Tag %d Proxy %s Domain %d Auth %s Interval %d",
- version, flags, route_tag, inet_ntoa (proxy), domain, authpass, interval);
-
- /* and the interface ptr, tick interval and flags */
- rl->iface = rp->iface;
- rl->rip_version = version;
- rl->interval = interval;
- rl->flags = (int16) (int) flags;
- rl->proxy_route = proxy;
- rl->route_tag = route_tag;
- rl->domain = domain;
- memcpy (rl->rip_auth_code, authpass, RIP_AUTH_SIZE);
-
- /* and set up the timer stuff */
- set_timer (&rl->rip_time, interval * 1000L);
- rl->rip_time.func = rip_shout;
- rl->rip_time.arg = rl;
- start_detached_timer (&rl->rip_time);
- return 1;
- }
-
-
-
- /* add a gateway to the rip_refuse list which allows us to ignore their
- * advertisements
- */
-
- int
- riprefadd (uint32 gateway)
- {
- register struct rip_refuse *rl;
-
- for (rl = Rip_refuse; rl != NULLREF; rl = rl->next)
- if (rl->target == gateway)
- return 0; /* Already in table */
-
- /* get a chunk of memory for the rip interface descriptor */
- rl = (struct rip_refuse *) callocw (1, sizeof (struct rip_refuse));
-
- /* tack this record on as the first in the list */
- rl->next = Rip_refuse;
- if (rl->next != NULLREF)
- rl->next->prev = rl;
- Rip_refuse = rl;
-
- /* fill in the gateway to ignore */
- rl->target = gateway;
- return 0;
- }
-
-
-
- /* Add an authentication type to an interface name */
-
- int
- ripauthadd (char const *ifcname, int16 domain, char const *password)
- {
- register struct rip_auth *ra;
- int x;
-
- for (ra = Rip_auth; ra != NULLAUTH; ra = ra->next)
- if (!strcmp (ifcname, ra->ifc_name) && (ra->domain == domain))
- return 1; /* Already in table */
-
- /* get a chunk of memory for the rip interface descriptor */
- ra = (struct rip_auth *) callocw (1, sizeof (struct rip_auth));
-
- /* tack this record on as the first in the list */
- ra->next = Rip_auth;
- if (ra->next != NULLAUTH)
- ra->next->prev = ra;
- Rip_auth = ra;
-
- /* fill in the data */
- ra->ifc_name = strdup (ifcname);
- ra->domain = domain;
- for (x = 0; x < RIP_AUTH_SIZE + 1; x++)
- ra->rip_auth_code[x] = '\0';
- strncpy (ra->rip_auth_code, password, RIP_AUTH_SIZE);
- return 0;
- }
-
-
-
- /* Drop an authentication to an interface name */
-
- int
- ripauthdrop (char *ifcname, int16 domain)
- {
- register struct rip_auth *ra;
-
- for (ra = Rip_auth; ra != NULLAUTH; ra = ra->next)
- if (!strcmp (ifcname, ra->ifc_name) && (ra->domain == domain))
- break;
-
- /* leave if we didn't find it */
- if (ra == NULLAUTH)
- return 0;
-
- /* Unlink from list */
- if (ra->next != NULLAUTH)
- ra->next->prev = ra->prev;
- if (ra->prev != NULLAUTH)
- ra->prev->next = ra->next;
- else
- Rip_auth = ra->next;
-
- free ((char *) ra->ifc_name);
- free ((char *) ra);
- return 0;
- }
-
-
-
- /* drop a RIP target */
-
- int
- rip_drop (uint32 dest, int16 domain)
- {
- register struct rip_list *rl;
-
- for (rl = Rip_list; rl != NULLRL; rl = rl->next)
- if ((rl->dest == dest) && (rl->domain == domain))
- break;
-
- /* leave if we didn't find it */
- if (rl == NULLRL)
- return 0;
-
- /* stop the timer */
- stop_timer (&rl->rip_time);
-
- /* Unlink from list */
- if (rl->next != NULLRL)
- rl->next->prev = rl->prev;
- if (rl->prev != NULLRL)
- rl->prev->next = rl->next;
- else
- Rip_list = rl->next;
-
- /* and deallocate the descriptor memory */
- free ((char *) rl);
- return 0;
- }
-
-
-
- /* drop a RIP-refuse target from the rip_refuse list */
-
- int
- riprefdrop (uint32 gateway)
- {
- register struct rip_refuse *rl;
-
- for (rl = Rip_refuse; rl != NULLREF; rl = rl->next)
- if (rl->target == gateway)
- break;
-
- /* leave if we didn't find it */
- if (rl == NULLREF)
- return 0;
-
- /* Unlink from list */
- if (rl->next != NULLREF)
- rl->next->prev = rl->prev;
- if (rl->prev != NULLREF)
- rl->prev->next = rl->next;
- else
- Rip_refuse = rl->next;
-
- /* and deallocate the structure memory */
- free ((char *) rl);
- return 0;
- }
-
-
-
- /* function to output a RIP CMD_RESPONSE packet for the rip_trigger list */
-
- static void
- rip_trigger ()
- {
- register struct rip_list *rl;
- int bits, i;
- struct route *rp;
-
- for (rl = Rip_list; rl != NULLRL; rl = rl->next)
- send_routes (rl->dest, RIP_PORT, 1, rl->flags, uchar(rl->rip_version), rl);
-
- /* Clear the trigger list */
- R_default.flags &= ~RTTRIG;
- for (bits = 0; bits < 32; bits++) {
- for (i = 0; i < HASHMOD; i++) {
- for (rp = Routes[bits][i]; rp != NULLROUTE; rp = rp->next)
- rp->flags &= ~RTTRIG;
- }
- }
- }
-
-
-
- /* Start RIP agent listening at local RIP UDP port */
- int
- rip_init ()
- {
- struct socket lsock;
-
- lsock.address = INADDR_ANY;
- lsock.port = RIP_PORT;
-
- if (Rip_cb == NULLUDP)
- Rip_cb = open_udp (&lsock, rip_rx);
-
- Rip_trace = 0;
-
- /* Add the 0 domain with no password */
-
- (void) ripauthadd (DEFAULTIFC, 0, RIP_NO_AUTH);
-
- return 0;
- }
-
-
-
- /* Process RIP input received from 'interface'. */
- static void
- rip_rx (struct iface *iface, struct udp_cb *sock, int16 cnt OPTIONAL)
- {
- int riplen = RIP_ENTRY;
- struct mbuf *bp;
- struct socket fsock;
- struct rip_refuse *rfl;
- struct rip_route entry;
- struct rip_head header;
- struct route *rp;
- struct rip_list *rl;
-
- /* receive the RIP packet */
- (void) recv_udp (sock, &fsock, &bp);
-
- /* check the gateway of this packet against the rip_refuse list and
- * discard it if a match is found
- */
-
- for (rfl = Rip_refuse; rfl != NULLREF; rfl = rfl->next) {
- if (fsock.address == rfl->target) {
- Rip_stat.refusals++;
- rip_trace (2, "RIP refused from %s", inet_ntoa (fsock.address));
- free_p (bp);
- return;
- }
- }
-
- pullheader (&header, &bp);
-
- /* This is a fiddle to make RIP98 look like latest rip +1 */
- if (header.rip_vers == RIP_VERSION_98) {
- header.rip_vers = RIP_VERSION_X;
- riplen = RIP98_ENTRY;
- }
-
- /* increment the rcvd cnt */
- Rip_stat.vdata[header.rip_vers].rcvd++;
-
- /* Check to see if we'll accept this version on this interface */
-
- if (header.rip_vers <= Rip_ver_refuse) {
- if (header.rip_vers != RIP_VERSION_X)
- rip_trace (3, "RIP version %d refused from [%s]", header.rip_vers, inet_ntoa (fsock.address));
- else
- rip_trace (3, "RIP version %d refused from [%s]", RIP_VERSION_98, inet_ntoa (fsock.address));
- Rip_stat.refusals++;
- free_p (bp);
- return;
- }
-
- if ((header.rip_vers == RIP_VERSION_X) && rip98allow == 0) {
- rip_trace (1, "RIP-98 frame received - rip98rx off");
- Rip_stat.refusals++;
- free_p(bp);
- return;
- }
-
- /* Check the version of the frame */
- switch (header.rip_vers) {
- case RIP_VERSION_2:
- break;
-
- case RIP_VERSION_0:
- rip_trace (1, "RIP Version 0 refused from [%s]", inet_ntoa (fsock.address));
- Rip_stat.version++;
- free_p (bp);
- return;
-
- case RIP_VERSION_1:
- /* Toss RIP if header is bogus for V1 */
- if (header.rip_domain != 0) {
- rip_trace (1, "RIP-1 bogus header, data in null fields from [%s]",
- inet_ntoa (fsock.address));
- Rip_stat.vdata[RIP_VERSION_1].unknown++;
- free_p (bp);
- return;
- }
- break;
-
- case RIP_VERSION_X:
-
- if (header.rip_cmd != RIPCMD_RESPONSE) {
- rip_trace (1, "RIP-98 invalid header received from [%s]\n", inet_ntoa (fsock.address));
- Rip_stat.vdata[RIP_VERSION_X].unknown++;
- free_p (bp);
- return;
- }
- break;
- default:
- break;
- }
- if (header.rip_vers != RIP_VERSION_X)
- rip_trace (2, "RIP Packet version %d processing", header.rip_vers);
- else
- rip_trace (2, "RIP Packet version %d processing", RIP_VERSION_98);
-
- switch (header.rip_cmd) {
- case RIPCMD_RESPONSE:
- rip_trace (2, "RIPCMD_RESPONSE from [%s] domain %d", inet_ntoa (fsock.address),
- header.rip_domain);
-
- Rip_stat.vdata[header.rip_vers].response++;
- if (header.rip_vers != RIP_VERSION_X)
- pullentry (&entry, &bp);
- else
- pull98entry (&entry, &bp);
-
- if (header.rip_vers >= RIP_VERSION_2) {
- if (header.rip_vers != RIP_VERSION_X) {
- /* We still have the authentication entry from above */
-
- if (!check_authentication (Rip_auth, &bp, &header, fsock.address,
- iface->name, (struct rip_authenticate *) &entry)) {
- free_p (bp);
- return;
- }
- }
- }
- proc_rip (iface, fsock.address, &entry, header.rip_vers);
-
- while (len_p (bp) >= riplen) {
- if (header.rip_vers != RIP_VERSION_X)
- pullentry (&entry, &bp);
- else
- pull98entry (&entry, &bp);
-
- proc_rip (iface, fsock.address, &entry, header.rip_vers);
- }
-
- /* If we can't reach the sender of this update, or if
- * our existing route is not through the interface we
- * got this update on, add him as a host specific entry
- */
- if ((rp = rt_blookup (fsock.address, 32)) != NULLROUTE)
- /* Host-specific route already exists, refresh it */
- start_detached_timer (&rp->timer);
- else if ((rp = rt_lookup (fsock.address)) == NULLROUTE
- || rp->iface != iface) {
- entry.rip_family = RIP_AF_INET;
- entry.rip_tag = 0;
- entry.rip_dest = fsock.address;
- if (header.rip_vers > RIP_VERSION_1)
- entry.rip_dest_mask = 32;
- else
- entry.rip_dest_mask = 0;
- entry.rip_router = 0;
- entry.rip_metric = 0; /* will get incremented to 1 */
- proc_rip (iface, fsock.address, &entry, header.rip_vers);
- }
- if (Rip_merge)
- rt_merge (Rip_trace);
- rip_trigger ();
- break;
-
- case RIPCMD_REQUEST:
- rip_trace (2, "RIPCMD_REQUEST from [%s] domain %d", inet_ntoa (fsock.address),
- header.rip_domain);
-
- Rip_stat.vdata[header.rip_vers].request++;
- /* For now, just send the whole table with split horizon
- * enabled when the source port is RIP_PORT, and send
- * the whole table with split horizon disable when another
- * source port is used. This should be replaced with a more
- * complete implementation that checks for non-global requests
- */
-
- if (header.rip_vers > RIP_VERSION_1) {
- /* RIP-2, let's see if we know something about this guy */
- rp = rt_lookup (fsock.address);
- for (rl = Rip_list; rl != NULLRL; rl = rl->next)
- if ((rl->dest == rp->target) && (rl->domain == header.rip_domain))
- break;
-
- if (rl == NULLRL)
- if (fsock.port == RIP_PORT)
- send_routes (fsock.address, fsock.port, 0, (RIP_BROADCAST | RIP_SPLIT |
- RIP_POISON), header.rip_vers, NULLRL);
- else
- send_routes (fsock.address, fsock.port, 0, (RIP_BROADCAST),
- header.rip_vers, NULLRL);
- else if (fsock.port == RIP_PORT)
- send_routes (fsock.address, fsock.port, 0, rl->flags, header.rip_vers, rl);
- else
- send_routes (fsock.address, fsock.port, 0, (rl->flags & ~(RIP_SPLIT | RIP_POISON)),
- header.rip_vers, rl);
- } else {
- if (fsock.port == RIP_PORT)
- send_routes (fsock.address, fsock.port, 0, (RIP_SPLIT | RIP_BROADCAST | RIP_POISON),
- header.rip_vers, NULLRL);
- else
- send_routes (fsock.address, fsock.port, 0, RIP_BROADCAST,
- header.rip_vers, NULLRL);
- }
- break;
-
- default:
- rip_trace (1, "RIPCMD Unknown or not implemented from [%s] command %d",
- inet_ntoa (fsock.address), header.rip_cmd);
- Rip_stat.vdata[header.rip_vers].unknown++;
- break;
- } /* switch */
- free_p (bp);
- }
-
-
-
- /* Apply a set of heuristics for determining the number of significant bits
- * (i.e., the address mask) in the target address. Needed since RIP doesn't
- * include the address mask for each entry. Applies only to RIP-1 packets.
- */
-
- static int
- nbits (uint32 target)
- {
- int bits = 0;
-
- if (target == 0)
- return 0; /* Special case: 0.0.0.0 is the default route */
-
- /* Check the host-part bytes of
- * the address to check for byte-wide zeros
- * which we'll consider to be subnet routes.
- * e.g. 44.80.0.0 will be considered to be equal to 44.80/16
- * whereas 44.80.1.0 will be considered to be 44.80.1/24
- */
- switch (hibyte (hiword (target)) >> 6) {
- case 3: /* Class C address */
- /* is it a host address ? i.e. are there any 1's in the host part ? */
- if (target & 0xff)
- bits = 32;
- else
- bits = 24;
- break;
- case 2: /* Class B address */
- if (target & 0xff)
- bits = 32;
- else if (target & 0xff00)
- bits = 24;
- else
- bits = 16;
- break;
- case 0: /* Class A address */
- case 1:
- default:
- if (target & 0xff)
- bits = 32;
- else if (target & 0xff00)
- bits = 24;
- else if (target & 0xff0000L)
- bits = 16;
- else
- bits = 8;
- }
-
- return bits;
- }
-
-
-
- /* Remove and process a RIP response entry from a packet */
-
- static void
- proc_rip (struct iface *iface, uint32 gateway, register struct rip_route *ep, unsigned char version)
- {
- int32 interval;
- uint32 target;
- unsigned int bits;
- register struct route *rp;
- struct rip_list *rl;
- int add = 0; /* action flags */
- int drop = 0;
- int trigger = 0;
-
- if (ep->rip_family != RIP_AF_INET) {
- if (ep->rip_family == RIP_AF_AUTH)
- return;
- /* Skip non-IP addresses */
- rip_trace (1, "RIP_rx: Not an IP family packet\n");
- Rip_stat.addr_family++;
- return;
- }
-
- /* RIP-1 says all unused fields must be zero */
- if (version == RIP_VERSION_1) {
- if (ep->rip_tag != 0 || ep->rip_dest_mask != 0 || ep->rip_router != 0) {
- rip_trace (1, "RIP_rx: RIP-1 entry bad, data in null fields");
- Rip_stat.vdata[version].unknown++;
- }
- /* Guess at the mask, since it's not explicit for RIP-1 */
- bits = (unsigned int) nbits (ep->rip_dest);
- target = ep->rip_dest;
- } else {
- /* Assume RIP-2 */
- if (!ep->rip_dest_mask) {
- /* No netmask, guess */
- bits = (unsigned int) nbits (ep->rip_dest);
- } else
- bits = (unsigned int) mask2width (ep->rip_dest_mask);
- target = ep->rip_dest;
- /* Check for "proxy" rip */
- if (ep->rip_router) {
- rip_trace (3, "Proxy rip pointing to [%s]", inet_ntoa (ep->rip_router));
- gateway = ep->rip_router;
- }
- }
-
- /* Don't ever add a route to myself through somebody! */
- if (bits == 32 && ismyaddr (target) != NULLIF) {
- rip_trace (2, "Route to self [%s]/32 metric %lu", inet_ntoa (target),
- ep->rip_metric);
- return;
- }
- /* Check to see if we'll take a default route, zero bits mean default */
-
- if (Rip_default_refuse && bits == 0) {
- rip_trace (2, "Default route refused from [%s]", inet_ntoa (target));
- return;
- }
- /* Update metric to reflect link cost */
- ep->rip_metric += iface->iface_metric;
- ep->rip_metric = min (ep->rip_metric, RIP_METRIC_UNREACHABLE);
-
- /* Find existing entry, if any */
- rp = rt_blookup (target, bits);
-
- /* Don't touch private routes */
- if (rp != NULLROUTE && (rp->flags & RTPRIVATE)) {
- rip_trace (3, "Route to [%s]/%u unchanged, private", inet_ntoa (target),
- bits);
- return;
- }
- if (rp == NULLROUTE) {
- if (ep->rip_metric < RIP_METRIC_UNREACHABLE) {
- /* New route; add it and trigger an update */
- add++;
- trigger++;
- }
- } else if (rp->metric == RIP_METRIC_UNREACHABLE) {
- /* Route is in hold-down; ignore this guy */
- rip_trace (2, "Route to [%s]/%u ignored (hold-down) metric %lu",
- inet_ntoa (target), bits, ep->rip_metric);
- } else if (rp->gateway == gateway && rp->iface == iface) {
- /* This is the gateway for the entry we already have, restart the timer */
- start_detached_timer (&rp->timer);
- if (rp->metric != ep->rip_metric) {
- /* Metric has changed. Update it and trigger an
- * update. If route has become unavailable, start
- * the hold-down timeout.
- */
- rip_trace (3, "Metric change [%s]/%u %lu -> %lu", inet_ntoa (target),
- bits, rp->metric, ep->rip_metric);
- if (ep->rip_metric == RIP_METRIC_UNREACHABLE)
- rt_timeout (rp); /* Enter hold-down timeout */
- else
- rp->metric = ep->rip_metric;
- trigger++;
- }
- } else {
- /* Entry is from a different gateway than the current route */
- if (ep->rip_metric < rp->metric) {
- /* Switch to a new gateway */
- rip_trace (3, "Metric better [%s]/%u new: %lu old: %lu", inet_ntoa (target),
- bits, ep->rip_metric, rp->metric);
- drop++;
- add++;
- trigger++;
- } else {
- /* Metric is no better, stay with current route */
- rip_trace (3, "Metric not better [%s]/%u new: %lu old: %lu", inet_ntoa (target),
- bits, ep->rip_metric, rp->metric);
- }
- }
- if (drop) {
- /* Switching to a better gateway; delete old entry */
- rip_trace (2, "Route drop [%s]/%u", inet_ntoa (target), bits);
- (void) rt_drop (target, bits);
- }
- if (add) {
- /* Add a new entry */
- interval = Rip_ttl;
- for (rl = Rip_list; rl != NULLRL; rl = rl->next) {
- if (rl->iface == iface) {
- interval = rl->interval * 4;
- break;
- }
- }
- rip_trace (2, "Route add [%s]/%u through %s via ",
- inet_ntoa (target), bits, iface->name);
- rip_trace (2, "[%s] metric %lu", inet_ntoa (gateway),
- ep->rip_metric);
-
- rp = rt_add (target, (unsigned) bits, gateway, iface,
- (int) ep->rip_metric, interval, 0);
-
- /* Add in the routing tag for RIP-2 */
-
- if (version >= RIP_VERSION_2)
- rp->route_tag = ep->rip_tag;
- else
- rp->route_tag = 0;
- }
- /* If the route changed, mark it for a triggered update */
- if (trigger && rp)
- rp->flags |= RTTRIG;
- }
-
-
-
- /* Send a RIP request packet to the specified destination */
-
- int
- ripreq (uint32 dest, int16 replyport, int16 version)
- {
- struct mbuf *bp;
- struct socket lsock, fsock;
- char *cp;
- register struct rip_list *rl;
-
- lsock.address = INADDR_ANY;
- lsock.port = replyport;
-
- /* if we were given a valid dest addr, ask it (the routers on that net)
- * for a default gateway
- */
-
- if (dest == 0)
- return 0;
-
- fsock.address = dest;
- fsock.port = RIP_PORT;
-
- /* Send out one RIP Request packet as a broadcast to 'dest' */
- if ((bp = alloc_mbuf (RIP_HEADER + RIP_ENTRY)) == NULLBUF)
- return -1;
-
- /* Check to see if we already know something about who we're */
- /* requesting the RIP from */
-
- for (rl = Rip_list; rl != NULLRL; rl = rl->next)
- if (rl->dest == dest)
- break;
-
- bp->cnt = RIP_HEADER + RIP_ENTRY;
- if (rl != NULLRL) {
- cp = putheader (bp->data, RIPCMD_REQUEST, rl->rip_version, rl->domain);
- if (rl->rip_version >= RIP_VERSION_2) {
- if (rl->flags & RIP_AUTHENTICATE) {
- cp = putauth (cp, RIP_AUTH_SIMPLE, rl->rip_auth_code);
- bp->cnt += RIP_ENTRY;
- }
- }
- Rip_stat.vdata[(int) rl->rip_version].output++;
- } else {
- cp = putheader (bp->data, RIPCMD_REQUEST, (char) version, 0);
- Rip_stat.vdata[version].output++;
- }
-
- cp = putentry (cp, 0, 0, 0L, 0L, 0L, RIP_METRIC_UNREACHABLE);
- (void) send_udp (&lsock, &fsock, 0, 0, bp, bp->cnt, 0, 0);
- return 0;
- }
-
-
-
- /* Write the authentication packet */
-
- static char *
- putauth (register char *cptr, int16 authtype, char *authpass)
- {
- int x;
- unsigned char *cp = (unsigned char *) cptr;
-
- cp = put16 (cp, 0xFFFF);
- cp = put16 (cp, authtype);
-
- /* Put the password in big-endian (network) byte order */
- /* This probably is not the best way to do this, since it */
- /* would hose up on a real big-endian machine. Oh well */
- /* Something to fix in the future. Whip me, beat me, make */
- /* me use an Intel micro brain. -N0POY */
-
- for (x = 0; x < RIP_AUTH_SIZE; x += 4) {
- *cp++ = uchar (authpass[x + 3]);
- *cp++ = uchar (authpass[x + 2]);
- *cp++ = uchar (authpass[x + 1]);
- *cp++ = uchar (authpass[x]);
- }
- return ((char *) cp);
- }
-
-
-
- /* Write the header of a RIP packet */
-
- static char *
- putheader (register unsigned char *cp, char command, char version, int16 domain)
- {
- *cp++ = uchar(command);
- *cp++ = uchar(version);
- return (char *) put16 ((unsigned char *) cp, domain);
- }
-
-
-
- /* Write a single entry into a rip packet */
-
- static char *
- putentry (register char *cptr, int16 fam, int16 tag, uint32 target, uint32 targmask,
- uint32 router, int32 metric)
- {
- unsigned char *cp = (unsigned char *) cptr;
-
- cp = put16 (cp, fam);
- cp = put16 (cp, tag);
- cp = put32 (cp, target);
- cp = put32 (cp, targmask);
- cp = put32 (cp, router);
- return (char *) put32 (cp, (unsigned long) metric);
- }
-
-
-
- /* Write a single entry into a rip98 packet */
-
- static char *
- put98entry (register char *cptr, uint32 target, int16 bits, int metric)
- {
- unsigned char *cp = (unsigned char *) cptr;
-
- cp = put32 (cp, target);
- *cp++ = uchar (bits);
- *cp++ = uchar (metric);
- return (char *) cp;
- }
-
-
-
- /* Check the authentication of RIP-II packets */
- int
- check_authentication (struct rip_auth *auth, struct mbuf **bpp OPTIONAL, struct rip_head *header,
- uint32 srcaddr, char const *ifcname, struct rip_authenticate *entry)
- {
- struct rip_auth *rd;
-
- for (rd = auth; rd != NULLAUTH; rd = rd->next) {
- if ((strcmp (ifcname, rd->ifc_name) == 0) ||
- (strcmp (DEFAULTIFC, rd->ifc_name) == 0)) {
- if (rd->domain == header->rip_domain) {
- /* We'll take this domain, check against a NULL password */
- if (strcmp (rd->rip_auth_code, RIP_NO_AUTH) == 0) {
- rip_trace (3, "RIP-2 taken due to no password from [%s] domain %d",
- inet_ntoa (srcaddr), header->rip_domain);
- return (TRUE);
- } else {
-
- /* Okay, we need an authentication */
-
- if (entry->rip_family != RIP_AF_AUTH) {
- /* It doesn't have an authentication packet */
- rip_trace (2, "RIP-2 lacking authentication packet from [%s] domain %d",
- inet_ntoa (srcaddr), header->rip_domain);
- Rip_stat.auth_fail++;
- return (FALSE);
- }
- if (entry->rip_auth_type != RIP_AUTH_SIMPLE) {
- /* Only support simple authentication */
- rip_trace (2, "RIP-2 wrong type of authentication from [%s]",
- inet_ntoa (srcaddr));
- Rip_stat.auth_fail++;
- return (FALSE);
- }
- if (memcmp (rd->rip_auth_code, entry->rip_auth_str, RIP_AUTH_SIZE) == 0) {
- rip_trace (3, "RIP-2 authenticated from [%s] domain %d",
- inet_ntoa (srcaddr), header->rip_domain);
- return (TRUE);
- } else {
- rip_trace (2, "RIP-2 authentication failed from [%s] domain %d,\n attempted password '%.16s' right password '%.16s'",
- inet_ntoa (srcaddr), header->rip_domain, entry->rip_auth_str, rd->rip_auth_code);
- Rip_stat.auth_fail++;
- return (FALSE);
- }
- }
- }
- }
- }
- /* Didn't find the right routing domain for this packet */
- rip_trace (3, "RIP-2 domain %d not accepted from [%s]", header->rip_domain,
- inet_ntoa (srcaddr));
- Rip_stat.wrong_domain++;
- return (FALSE);
- }
-
-
-
- /* Route timeout handler. If route has already been marked for deletion
- * then delete it. Otherwise mark for deletion and restart timer.
- */
- void
- rt_timeout (void *s)
- {
- register struct route *rp = (struct route *) s;
-
- stop_timer (&rp->timer);
- if (rp->metric < RIP_METRIC_UNREACHABLE) {
- rip_trace (5, "RIP: route to [%s]/%d expired - hold down", inet_ntoa (rp->target),
- rp->bits);
- rp->metric = RIP_METRIC_UNREACHABLE;
- if (dur_timer (&rp->timer) == 0)
- set_timer (&rp->timer, Rip_ttl * 1000L);
- /* wait 2/3 of timeout before garbage collect */
- set_timer (&rp->timer, dur_timer (&rp->timer) * 2 / 3);
- rp->timer.func = rt_timeout;
- rp->timer.arg = (void *) rp;
- start_detached_timer (&rp->timer);
- /* Route changed; mark it for triggered update */
- rp->flags |= RTTRIG;
- rip_trigger ();
- } else {
- rip_trace (5, "RIP: route to [%s]/%d expired - dropped", inet_ntoa (rp->target),
- rp->bits);
- (void) rt_drop (rp->target, rp->bits);
- }
- }
-
-
-
- void
- pullheader (struct rip_head *ep, struct mbuf **bpp)
- {
- ep->rip_cmd = uchar(pullchar (bpp));
- ep->rip_vers = uchar(pullchar (bpp));
- ep->rip_domain = pull16 (bpp);
- }
-
-
-
- void
- rip_trace (short level, char const *errstr,...)
- {
- if ((int16) level <= Rip_trace) {
- char *timestr;
- time_t timer;
- va_list argptr;
-
- if (Rip_trace_fname != NULLCHAR) {
- (void) time (&timer);
- timestr = ctime (&timer);
- timestr[24] = '\0';
- Rip_trace_file = fopen (Rip_trace_fname, APPEND_TEXT);
- fprintf (Rip_trace_file, "%s - ", timestr);
-
- va_start (argptr, errstr); /*lint !e718 !e746 */
- (void) vfprintf (Rip_trace_file, errstr, argptr);
- va_end (argptr);
- fprintf (Rip_trace_file, "\n");
- (void) fclose (Rip_trace_file);
- } else {
- va_start (argptr, errstr);
- (void) usvprintf (Curproc->output, errstr, argptr);
- va_end (argptr);
- tprintf ("\n");
- }
- }
- }
-
-
-
- void
- pullentry (register struct rip_route *ep, struct mbuf **bpp)
- {
- ep->rip_family = pull16 (bpp);
- ep->rip_tag = pull16 (bpp);
- ep->rip_dest = pull32 (bpp);
- ep->rip_dest_mask = pull32 (bpp);
- ep->rip_router = pull32 (bpp);
- ep->rip_metric = (long) pull32 (bpp);
- }
-
-
-
- /* Pulls a RIP98 Entry and converts to "more standard" RIP */
- void
- pull98entry (register struct rip_route *ep, struct mbuf **bpp)
- {
- unsigned int mask_bits;
- int metric_bits;
-
- ep->rip_family = RIP_AF_INET;
- ep->rip_tag = 0;
- ep->rip_dest = pull32 (bpp);
- mask_bits = (unsigned int) pullchar (bpp);
- metric_bits = pullchar (bpp);
- ep->rip_dest_mask = (0xffffffffLU << (32-mask_bits));
- ep->rip_metric = metric_bits;
- ep->rip_router = 0;
-
- #if 0 /* Used in debugging, but left for anyone who has need ! */
- rip_trace (9, "Rip98: Destination [%s] : mask %d : metric %d", inet_ntoa( ep->rip_dest), mask_bits, metric_bits);
- #endif
- }
-
-
- #endif /* RIP */
-